{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Pandas 字符串拼接\n",
        "\n",
        "本教程介绍Pandas中字符串拼接的相关知识点和代码案例，包括`str.cat()`、`str.join()`方法和使用`+`运算符进行字符串拼接。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 学习目标\n",
        "\n",
        "- 掌握`str.cat()`方法连接字符串序列\n",
        "- 掌握`str.join()`方法使用分隔符连接字符串\n",
        "- 了解使用`+`运算符进行字符串拼接\n",
        "- 了解不同拼接方法的适用场景\n",
        "- 学习实际应用案例\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 1. 导入库\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "import numpy as np\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2. str.cat() - 连接字符串序列\n",
        "\n",
        "`str.cat()`方法用于连接Series中的字符串，可以连接同一Series的元素，也可以连接多个Series。\n",
        "\n",
        "### 语法\n",
        "```python\n",
        "series.str.cat(others=None, sep='', na_rep=None, join='left')\n",
        "```\n",
        "\n",
        "### 参数\n",
        "- `others`: 要连接的其他Series或列表，默认为None（连接同一Series的元素）\n",
        "- `sep`: 分隔符，默认为空字符串\n",
        "- `na_rep`: NaN值的替换字符串，默认为None（保留NaN）\n",
        "- `join`: 连接方式（'left', 'right', 'outer', 'inner'），默认为'left'\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.1 连接同一Series的元素\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "0    Python\n",
            "1    Pandas\n",
            "2     NumPy\n",
            "dtype: object\n",
            "\n",
            "连接所有元素（无分隔符）:\n",
            "PythonPandasNumPy\n",
            "\n",
            "使用分隔符'-'连接:\n",
            "Python-Pandas-NumPy\n",
            "\n",
            "使用' | '作为分隔符:\n",
            "Python | Pandas | NumPy\n"
          ]
        }
      ],
      "source": [
        "# 创建示例数据\n",
        "data = pd.Series(['Python', 'Pandas', 'NumPy'])\n",
        "print(\"原始数据:\")\n",
        "print(data)\n",
        "\n",
        "# 使用cat()连接所有元素（默认无分隔符）\n",
        "result = data.str.cat()\n",
        "print(\"\\n连接所有元素（无分隔符）:\")\n",
        "print(result)\n",
        "\n",
        "# 使用分隔符连接\n",
        "result_sep = data.str.cat(sep='-')\n",
        "print(\"\\n使用分隔符'-'连接:\")\n",
        "print(result_sep)\n",
        "\n",
        "# 使用多个字符作为分隔符\n",
        "result_multi = data.str.cat(sep=' | ')\n",
        "print(\"\\n使用' | '作为分隔符:\")\n",
        "print(result_multi)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.2 连接多个Series（按行连接）\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "第一个Series:\n",
            "0    John\n",
            "1    Jane\n",
            "2     Bob\n",
            "dtype: object\n",
            "\n",
            "第二个Series:\n",
            "0        Doe\n",
            "1      Smith\n",
            "2    Johnson\n",
            "dtype: object\n",
            "\n",
            "连接两个Series（无分隔符）:\n",
            "0       JohnDoe\n",
            "1     JaneSmith\n",
            "2    BobJohnson\n",
            "dtype: object\n",
            "\n",
            "使用空格分隔符连接:\n",
            "0       John Doe\n",
            "1     Jane Smith\n",
            "2    Bob Johnson\n",
            "dtype: object\n",
            "\n",
            "连接多个Series:\n",
            "0       John, Doe, 30\n",
            "1     Jane, Smith, 25\n",
            "2    Bob, Johnson, 35\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建多个Series\n",
        "first_names = pd.Series(['John', 'Jane', 'Bob'])\n",
        "last_names = pd.Series(['Doe', 'Smith', 'Johnson'])\n",
        "\n",
        "print(\"第一个Series:\")\n",
        "print(first_names)\n",
        "print(\"\\n第二个Series:\")\n",
        "print(last_names)\n",
        "\n",
        "# 连接两个Series（按行连接，无分隔符）\n",
        "result = first_names.str.cat(last_names)\n",
        "print(\"\\n连接两个Series（无分隔符）:\")\n",
        "print(result)\n",
        "\n",
        "# 使用分隔符连接\n",
        "result_sep = first_names.str.cat(last_names, sep=' ')\n",
        "print(\"\\n使用空格分隔符连接:\")\n",
        "print(result_sep)\n",
        "\n",
        "# 连接多个Series\n",
        "ages = pd.Series(['30', '25', '35'])\n",
        "result_multi = first_names.str.cat([last_names, ages], sep=', ')\n",
        "print(\"\\n连接多个Series:\")\n",
        "print(result_multi)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.3 处理NaN值（na_rep参数）\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "第一个Series:\n",
            "0    Python\n",
            "1    Pandas\n",
            "2       NaN\n",
            "3     NumPy\n",
            "dtype: object\n",
            "\n",
            "第二个Series:\n",
            "0    Programming\n",
            "1            NaN\n",
            "2       Analysis\n",
            "3         Arrays\n",
            "dtype: object\n",
            "\n",
            "默认处理（NaN保留）:\n",
            "0    Python-Programming\n",
            "1                   NaN\n",
            "2                   NaN\n",
            "3          NumPy-Arrays\n",
            "dtype: object\n",
            "\n",
            "指定NaN替换为'N/A':\n",
            "0    Python-Programming\n",
            "1            Pandas-N/A\n",
            "2          N/A-Analysis\n",
            "3          NumPy-Arrays\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建包含NaN的数据\n",
        "data1 = pd.Series(['Python', 'Pandas', np.nan, 'NumPy'])\n",
        "data2 = pd.Series(['Programming', np.nan, 'Analysis', 'Arrays'])\n",
        "\n",
        "print(\"第一个Series:\")\n",
        "print(data1)\n",
        "print(\"\\n第二个Series:\")\n",
        "print(data2)\n",
        "\n",
        "# 默认情况下，NaN会保留\n",
        "result_default = data1.str.cat(data2, sep='-')\n",
        "print(\"\\n默认处理（NaN保留）:\")\n",
        "print(result_default)\n",
        "\n",
        "# 指定NaN的替换值\n",
        "result_na = data1.str.cat(data2, sep='-', na_rep='N/A')\n",
        "print(\"\\n指定NaN替换为'N/A':\")\n",
        "print(result_na)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.4 连接方式（join参数）\n",
        "\n",
        "当连接多个Series时，如果索引不一致，可以使用`join`参数控制连接方式。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "第一个Series:\n",
            "0    A\n",
            "1    B\n",
            "2    C\n",
            "dtype: object\n",
            "\n",
            "第二个Series:\n",
            "1    X\n",
            "2    Y\n",
            "dtype: object\n",
            "\n",
            "使用'left'连接:\n",
            "0    NaN\n",
            "1    B-X\n",
            "2    C-Y\n",
            "dtype: object\n",
            "\n",
            "使用'outer'连接:\n",
            "0    NaN\n",
            "1    B-X\n",
            "2    C-Y\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建索引不一致的Series\n",
        "data1 = pd.Series(['A', 'B', 'C'], index=[0, 1, 2])\n",
        "data2 = pd.Series(['X', 'Y'], index=[1, 2])\n",
        "\n",
        "print(\"第一个Series:\")\n",
        "print(data1)\n",
        "print(\"\\n第二个Series:\")\n",
        "print(data2)\n",
        "\n",
        "# 默认使用'left'连接（以第一个Series的索引为准）\n",
        "result_left = data1.str.cat(data2, sep='-', join='left')\n",
        "print(\"\\n使用'left'连接:\")\n",
        "print(result_left)\n",
        "\n",
        "# 使用'outer'连接（保留所有索引）\n",
        "result_outer = data1.str.cat(data2, sep='-', join='outer')\n",
        "print(\"\\n使用'outer'连接:\")\n",
        "print(result_outer)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3. str.join() - 使用分隔符连接\n",
        "\n",
        "`str.join()`方法用于将Series中每个字符串的元素（字符）用指定的分隔符连接起来。\n",
        "\n",
        "### 语法\n",
        "```python\n",
        "series.str.join(sep)\n",
        "```\n",
        "\n",
        "### 参数\n",
        "- `sep`: 分隔符字符串\n",
        "\n",
        "### 注意\n",
        "`str.join()`与Python内置的`str.join()`不同，它是对Series中每个字符串的字符进行连接，而不是连接Series的元素。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "0    ABC\n",
            "1    XYZ\n",
            "2    123\n",
            "dtype: object\n",
            "\n",
            "使用'-'连接每个字符串的字符:\n",
            "0    A-B-C\n",
            "1    X-Y-Z\n",
            "2    1-2-3\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建示例数据\n",
        "data = pd.Series(['ABC', 'XYZ', '123'])\n",
        "print(\"原始数据:\")\n",
        "print(data)\n",
        "\n",
        "# 使用join()连接每个字符串的字符\n",
        "result = data.str.join('-')\n",
        "print(\"\\n使用'-'连接每个字符串的字符:\")\n",
        "print(result)\n",
        "\n",
        "# 注意：join()是对每个字符串内部的字符进行连接\n",
        "# 'ABC' -> 'A-B-C'\n",
        "# 'XYZ' -> 'X-Y-Z'\n",
        "# '123' -> '1-2-3'\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 3.1 实际应用：格式化字符串\n",
        "\n",
        "`str.join()`常用于格式化字符串，如在数字之间添加分隔符。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始电话号码:\n",
            "0    13812345678\n",
            "1    13987654321\n",
            "2    15011223344\n",
            "dtype: object\n",
            "\n",
            "格式化后的电话号码:\n",
            "0    1-3-8-1-2-3-4-5-6-7-8\n",
            "1    1-3-9-8-7-6-5-4-3-2-1\n",
            "2    1-5-0-1-1-2-2-3-3-4-4\n",
            "dtype: object\n",
            "\n",
            "格式化电话号码（每段用'-'分隔）:\n",
            "0    138-1234-5678\n",
            "1    139-8765-4321\n",
            "2    150-1122-3344\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建电话号码数据（无分隔符）\n",
        "phones = pd.Series(['13812345678', '13987654321', '15011223344'])\n",
        "print(\"原始电话号码:\")\n",
        "print(phones)\n",
        "\n",
        "# 使用join()在数字之间添加分隔符\n",
        "formatted = phones.str.join('-')\n",
        "print(\"\\n格式化后的电话号码:\")\n",
        "print(formatted)\n",
        "\n",
        "# 更实际的例子：在每3位数字之间添加空格\n",
        "# 注意：这需要先将字符串分割，然后join，但join()本身不支持这种操作\n",
        "# 可以使用其他方法实现\n",
        "phones_formatted = phones.str[:3] + '-' + phones.str[3:7] + '-' + phones.str[7:]\n",
        "print(\"\\n格式化电话号码（每段用'-'分隔）:\")\n",
        "print(phones_formatted)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 4. 使用 + 运算符拼接\n",
        "\n",
        "在Pandas中，可以直接使用`+`运算符进行字符串拼接，这是最直观的方法。\n",
        "\n",
        "### 4.1 基本用法\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "第一个Series:\n",
            "0    John\n",
            "1    Jane\n",
            "2     Bob\n",
            "dtype: object\n",
            "\n",
            "第二个Series:\n",
            "0        Doe\n",
            "1      Smith\n",
            "2    Johnson\n",
            "dtype: object\n",
            "\n",
            "使用+运算符拼接（无分隔符）:\n",
            "0       JohnDoe\n",
            "1     JaneSmith\n",
            "2    BobJohnson\n",
            "dtype: object\n",
            "\n",
            "使用+运算符拼接（带空格）:\n",
            "0       John Doe\n",
            "1     Jane Smith\n",
            "2    Bob Johnson\n",
            "dtype: object\n",
            "\n",
            "拼接多个Series:\n",
            "0       John Doe, Age: 30\n",
            "1     Jane Smith, Age: 25\n",
            "2    Bob Johnson, Age: 35\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建示例数据\n",
        "first_names = pd.Series(['John', 'Jane', 'Bob'])\n",
        "last_names = pd.Series(['Doe', 'Smith', 'Johnson'])\n",
        "\n",
        "print(\"第一个Series:\")\n",
        "print(first_names)\n",
        "print(\"\\n第二个Series:\")\n",
        "print(last_names)\n",
        "\n",
        "# 使用+运算符拼接（无分隔符）\n",
        "result = first_names + last_names\n",
        "print(\"\\n使用+运算符拼接（无分隔符）:\")\n",
        "print(result)\n",
        "\n",
        "# 使用+运算符拼接（带分隔符）\n",
        "result_sep = first_names + ' ' + last_names\n",
        "print(\"\\n使用+运算符拼接（带空格）:\")\n",
        "print(result_sep)\n",
        "\n",
        "# 拼接多个Series\n",
        "ages = pd.Series(['30', '25', '35'])\n",
        "result_multi = first_names + ' ' + last_names + ', Age: ' + ages\n",
        "print(\"\\n拼接多个Series:\")\n",
        "print(result_multi)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 4.2 在DataFrame中的应用\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始DataFrame:\n",
            "  first_name last_name  age\n",
            "0       John       Doe   30\n",
            "1       Jane     Smith   25\n",
            "2        Bob   Johnson   35\n",
            "\n",
            "拼接后的DataFrame:\n",
            "  first_name last_name  age    full_name                  info\n",
            "0       John       Doe   30     John Doe     John Doe, Age: 30\n",
            "1       Jane     Smith   25   Jane Smith   Jane Smith, Age: 25\n",
            "2        Bob   Johnson   35  Bob Johnson  Bob Johnson, Age: 35\n"
          ]
        }
      ],
      "source": [
        "# 创建DataFrame\n",
        "df = pd.DataFrame({\n",
        "    'first_name': ['John', 'Jane', 'Bob'],\n",
        "    'last_name': ['Doe', 'Smith', 'Johnson'],\n",
        "    'age': [30, 25, 35]\n",
        "})\n",
        "\n",
        "print(\"原始DataFrame:\")\n",
        "print(df)\n",
        "\n",
        "# 使用+运算符拼接列\n",
        "df['full_name'] = df['first_name'] + ' ' + df['last_name']\n",
        "df['info'] = df['full_name'] + ', Age: ' + df['age'].astype(str)\n",
        "\n",
        "print(\"\\n拼接后的DataFrame:\")\n",
        "print(df)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 4.3 处理NaN值\n",
        "\n",
        "使用`+`运算符时，如果遇到NaN，结果也会是NaN。可以使用`fillna()`或条件判断来处理。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "第一个Series:\n",
            "0    Python\n",
            "1    Pandas\n",
            "2       NaN\n",
            "3     NumPy\n",
            "dtype: object\n",
            "\n",
            "第二个Series:\n",
            "0    Programming\n",
            "1       Analysis\n",
            "2           Data\n",
            "3            NaN\n",
            "dtype: object\n",
            "\n",
            "使用+运算符拼接（NaN保留）:\n",
            "0    Python-Programming\n",
            "1       Pandas-Analysis\n",
            "2                   NaN\n",
            "3                   NaN\n",
            "dtype: object\n",
            "\n",
            "使用fillna()处理NaN后拼接:\n",
            "0    Python-Programming\n",
            "1       Pandas-Analysis\n",
            "2                 -Data\n",
            "3                NumPy-\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建包含NaN的数据\n",
        "data1 = pd.Series(['Python', 'Pandas', np.nan, 'NumPy'])\n",
        "data2 = pd.Series(['Programming', 'Analysis', 'Data', np.nan])\n",
        "\n",
        "print(\"第一个Series:\")\n",
        "print(data1)\n",
        "print(\"\\n第二个Series:\")\n",
        "print(data2)\n",
        "\n",
        "# 使用+运算符拼接（NaN会保留）\n",
        "result = data1 + '-' + data2\n",
        "print(\"\\n使用+运算符拼接（NaN保留）:\")\n",
        "print(result)\n",
        "\n",
        "# 使用fillna()处理NaN\n",
        "result_filled = data1.fillna('') + '-' + data2.fillna('')\n",
        "print(\"\\n使用fillna()处理NaN后拼接:\")\n",
        "print(result_filled)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 5. 方法对比总结\n",
        "\n",
        "| 方法 | 功能 | 适用场景 | 优点 | 缺点 |\n",
        "|------|------|---------|------|------|\n",
        "| `str.cat()` | 连接字符串序列 | 连接多个Series，需要控制NaN和连接方式 | 功能强大，支持多种连接方式 | 语法相对复杂 |\n",
        "| `str.join()` | 连接字符串的字符 | 格式化字符串，在字符间添加分隔符 | 简单直接 | 功能有限，只能连接字符 |\n",
        "| `+` 运算符 | 拼接字符串 | 简单拼接，最常用 | 直观易用，语法简单 | 处理NaN需要额外操作 |\n",
        "\n",
        "### 选择建议\n",
        "\n",
        "- **简单拼接**: 使用`+`运算符 - 最直观\n",
        "- **连接多个Series**: 使用`str.cat()` - 功能更强大\n",
        "- **格式化字符串**: 使用`str.join()`或`+`运算符\n",
        "- **需要控制NaN处理**: 使用`str.cat()`的`na_rep`参数\n",
        "- **需要控制连接方式**: 使用`str.cat()`的`join`参数\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 6. 实际应用案例\n",
        "\n",
        "### 6.1 生成完整姓名\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "  first_name last_name middle_name\n",
            "0       John       Doe           A\n",
            "1       Jane     Smith           B\n",
            "2        Bob   Johnson        None\n",
            "3       Mary  Williams           C\n",
            "\n",
            "拼接结果:\n",
            "  first_name last_name     full_name_v1     full_name_v2     full_name_v3\n",
            "0       John       Doe       John A Doe       John A Doe       John A Doe\n",
            "1       Jane     Smith     Jane B Smith     Jane B Smith     Jane B Smith\n",
            "2        Bob   Johnson      Bob Johnson      Bob Johnson     Bob  Johnson\n",
            "3       Mary  Williams  Mary C Williams  Mary C Williams  Mary C Williams\n"
          ]
        }
      ],
      "source": [
        "# 创建姓名数据\n",
        "df = pd.DataFrame({\n",
        "    'first_name': ['John', 'Jane', 'Bob', 'Mary'],\n",
        "    'last_name': ['Doe', 'Smith', 'Johnson', 'Williams'],\n",
        "    'middle_name': ['A', 'B', None, 'C']\n",
        "})\n",
        "\n",
        "print(\"原始数据:\")\n",
        "print(df)\n",
        "\n",
        "# 方法1：使用+运算符（处理middle_name为None的情况）\n",
        "df['full_name_v1'] = df['first_name'] + ' ' + df['middle_name'].fillna('') + ' ' + df['last_name']\n",
        "df['full_name_v1'] = df['full_name_v1'].str.replace('  ', ' ').str.strip()\n",
        "\n",
        "# 方法2：使用str.cat()（更优雅）\n",
        "df['full_name_v2'] = df[['first_name', 'middle_name', 'last_name']].apply(\n",
        "    lambda x: ' '.join(x.dropna().astype(str)), axis=1\n",
        ")\n",
        "\n",
        "# 方法3：使用str.cat()直接拼接\n",
        "df['full_name_v3'] = df['first_name'].str.cat([df['middle_name'], df['last_name']], \n",
        "                                                sep=' ', na_rep='')\n",
        "\n",
        "print(\"\\n拼接结果:\")\n",
        "print(df[['first_name', 'last_name', 'full_name_v1', 'full_name_v2', 'full_name_v3']])\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 6.2 生成完整地址\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始地址数据:\n",
            "        street       city      state zipcode\n",
            "0  123 Main St    Beijing    Beijing  100000\n",
            "1  456 Oak Ave   Shanghai   Shanghai  200000\n",
            "2  789 Pine Rd  Guangzhou  Guangdong  510000\n",
            "\n",
            "完整地址:\n",
            "        street       city                              full_address\n",
            "0  123 Main St    Beijing      123 Main St, Beijing, Beijing 100000\n",
            "1  456 Oak Ave   Shanghai    456 Oak Ave, Shanghai, Shanghai 200000\n",
            "2  789 Pine Rd  Guangzhou  789 Pine Rd, Guangzhou, Guangdong 510000\n"
          ]
        }
      ],
      "source": [
        "# 创建地址数据\n",
        "df = pd.DataFrame({\n",
        "    'street': ['123 Main St', '456 Oak Ave', '789 Pine Rd'],\n",
        "    'city': ['Beijing', 'Shanghai', 'Guangzhou'],\n",
        "    'state': ['Beijing', 'Shanghai', 'Guangdong'],\n",
        "    'zipcode': ['100000', '200000', '510000']\n",
        "})\n",
        "\n",
        "print(\"原始地址数据:\")\n",
        "print(df)\n",
        "\n",
        "# 使用+运算符拼接完整地址\n",
        "df['full_address'] = (df['street'] + ', ' + \n",
        "                     df['city'] + ', ' + \n",
        "                     df['state'] + ' ' + \n",
        "                     df['zipcode'])\n",
        "\n",
        "print(\"\\n完整地址:\")\n",
        "print(df[['street', 'city', 'full_address']])\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 6.3 格式化电话号码\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始电话号码:\n",
            "         phone\n",
            "0  13812345678\n",
            "1  13987654321\n",
            "2  15011223344\n",
            "\n",
            "格式化后的电话号码:\n",
            "         phone formatted_phone\n",
            "0  13812345678   138-1234-5678\n",
            "1  13987654321   139-8765-4321\n",
            "2  15011223344   150-1122-3344\n"
          ]
        }
      ],
      "source": [
        "# 创建电话号码数据\n",
        "df = pd.DataFrame({\n",
        "    'phone': ['13812345678', '13987654321', '15011223344']\n",
        "})\n",
        "\n",
        "print(\"原始电话号码:\")\n",
        "print(df)\n",
        "\n",
        "# 格式化电话号码（添加分隔符）\n",
        "df['formatted_phone'] = (df['phone'].str[:3] + '-' + \n",
        "                         df['phone'].str[3:7] + '-' + \n",
        "                         df['phone'].str[7:])\n",
        "\n",
        "print(\"\\n格式化后的电话号码:\")\n",
        "print(df)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 6.4 生成唯一标识符\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "  user_id department  year\n",
            "0    U001         IT  2024\n",
            "1    U002         HR  2024\n",
            "2    U003    Finance  2024\n",
            "\n",
            "生成的唯一标识符:\n",
            "  user_id department  year          unique_id\n",
            "0    U001         IT  2024       IT-U001-2024\n",
            "1    U002         HR  2024       HR-U002-2024\n",
            "2    U003    Finance  2024  Finance-U003-2024\n"
          ]
        }
      ],
      "source": [
        "# 创建用户数据\n",
        "df = pd.DataFrame({\n",
        "    'user_id': ['U001', 'U002', 'U003'],\n",
        "    'department': ['IT', 'HR', 'Finance'],\n",
        "    'year': [2024, 2024, 2024]\n",
        "})\n",
        "\n",
        "print(\"原始数据:\")\n",
        "print(df)\n",
        "\n",
        "# 生成唯一标识符：部门-用户ID-年份\n",
        "df['unique_id'] = df['department'] + '-' + df['user_id'] + '-' + df['year'].astype(str)\n",
        "\n",
        "print(\"\\n生成的唯一标识符:\")\n",
        "print(df)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 6.5 生成邮件内容\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "   name product  price\n",
            "0  John  Laptop    999\n",
            "1  Jane   Phone    599\n",
            "2   Bob  Tablet    399\n",
            "\n",
            "生成的邮件内容:\n",
            "\n",
            "--- Email 1 ---\n",
            "Dear John,\n",
            "\n",
            "Thank you for purchasing Laptop. Your order total is $999.\n",
            "\n",
            "Best regards,\n",
            "The Team\n",
            "\n",
            "--- Email 2 ---\n",
            "Dear Jane,\n",
            "\n",
            "Thank you for purchasing Phone. Your order total is $599.\n",
            "\n",
            "Best regards,\n",
            "The Team\n",
            "\n",
            "--- Email 3 ---\n",
            "Dear Bob,\n",
            "\n",
            "Thank you for purchasing Tablet. Your order total is $399.\n",
            "\n",
            "Best regards,\n",
            "The Team\n"
          ]
        }
      ],
      "source": [
        "# 创建邮件数据\n",
        "df = pd.DataFrame({\n",
        "    'name': ['John', 'Jane', 'Bob'],\n",
        "    'product': ['Laptop', 'Phone', 'Tablet'],\n",
        "    'price': [999, 599, 399]\n",
        "})\n",
        "\n",
        "print(\"原始数据:\")\n",
        "print(df)\n",
        "\n",
        "# 生成个性化邮件内容\n",
        "df['email_content'] = (\n",
        "    'Dear ' + df['name'] + ',\\n\\n' +\n",
        "    'Thank you for purchasing ' + df['product'] + \n",
        "    '. Your order total is $' + df['price'].astype(str) + '.\\n\\n' +\n",
        "    'Best regards,\\nThe Team'\n",
        ")\n",
        "\n",
        "print(\"\\n生成的邮件内容:\")\n",
        "for idx, content in df['email_content'].items():\n",
        "    print(f\"\\n--- Email {idx+1} ---\")\n",
        "    print(content)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 6.6 连接列表元素\n",
        "\n",
        "使用`str.cat()`可以连接Series中的所有元素为一个字符串。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "标签列表:\n",
            "0              Python\n",
            "1         Programming\n",
            "2        Data Science\n",
            "3    Machine Learning\n",
            "dtype: object\n",
            "\n",
            "连接所有标签:\n",
            "Python, Programming, Data Science, Machine Learning\n",
            "\n",
            "带换行的标签列表:\n",
            "Python\n",
            "Programming\n",
            "Data Science\n",
            "Machine Learning\n"
          ]
        }
      ],
      "source": [
        "# 创建标签数据\n",
        "tags = pd.Series(['Python', 'Programming', 'Data Science', 'Machine Learning'])\n",
        "\n",
        "print(\"标签列表:\")\n",
        "print(tags)\n",
        "\n",
        "# 连接所有标签为一个字符串\n",
        "all_tags = tags.str.cat(sep=', ')\n",
        "print(\"\\n连接所有标签:\")\n",
        "print(all_tags)\n",
        "\n",
        "# 创建带编号的标签列表\n",
        "numbered_tags = tags.str.cat(sep='\\n')\n",
        "print(\"\\n带换行的标签列表:\")\n",
        "print(numbered_tags)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 7. 注意事项和最佳实践\n",
        "\n",
        "### 7.1 str.cat() vs + 运算符\n",
        "\n",
        "- **str.cat()**: 适合连接多个Series，可以更好地控制NaN和连接方式\n",
        "- **+ 运算符**: 适合简单拼接，语法更直观\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "使用+运算符:\n",
            "0    A-X\n",
            "1    B-Y\n",
            "2    C-Z\n",
            "dtype: object\n",
            "\n",
            "使用str.cat():\n",
            "0    A-X\n",
            "1    B-Y\n",
            "2    C-Z\n",
            "dtype: object\n",
            "\n",
            "处理NaN:\n",
            "+运算符: ['A-X', 'B-Y', nan]\n",
            "str.cat(): ['A-X', 'B-Y', 'N/A-Z']\n"
          ]
        }
      ],
      "source": [
        "# 对比str.cat()和+运算符\n",
        "data1 = pd.Series(['A', 'B', 'C'])\n",
        "data2 = pd.Series(['X', 'Y', 'Z'])\n",
        "\n",
        "# 使用+运算符\n",
        "result1 = data1 + '-' + data2\n",
        "print(\"使用+运算符:\")\n",
        "print(result1)\n",
        "\n",
        "# 使用str.cat()\n",
        "result2 = data1.str.cat(data2, sep='-')\n",
        "print(\"\\n使用str.cat():\")\n",
        "print(result2)\n",
        "\n",
        "# 结果相同，但str.cat()在处理NaN时更方便\n",
        "data3 = pd.Series(['A', 'B', np.nan])\n",
        "result3_plus = data3 + '-' + data2  # NaN会保留\n",
        "result3_cat = data3.str.cat(data2, sep='-', na_rep='N/A')  # 可以替换NaN\n",
        "\n",
        "print(\"\\n处理NaN:\")\n",
        "print(\"+运算符:\", result3_plus.tolist())\n",
        "print(\"str.cat():\", result3_cat.tolist())\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 7.2 性能考虑\n",
        "\n",
        "对于大量数据，`str.cat()`通常比`+`运算符性能更好，特别是连接多个Series时。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "数据量: 10000\n",
            "+运算符耗时: 0.0007秒\n",
            "str.cat()耗时: 0.0012秒\n"
          ]
        }
      ],
      "source": [
        "# 性能测试示例（实际测试可能需要大量数据）\n",
        "import time\n",
        "\n",
        "# 创建大量数据\n",
        "n = 10000\n",
        "data1 = pd.Series(['A'] * n)\n",
        "data2 = pd.Series(['B'] * n)\n",
        "\n",
        "# 测试+运算符\n",
        "start = time.time()\n",
        "result1 = data1 + '-' + data2\n",
        "time_plus = time.time() - start\n",
        "\n",
        "# 测试str.cat()\n",
        "start = time.time()\n",
        "result2 = data1.str.cat(data2, sep='-')\n",
        "time_cat = time.time() - start\n",
        "\n",
        "print(f\"数据量: {n}\")\n",
        "print(f\"+运算符耗时: {time_plus:.4f}秒\")\n",
        "print(f\"str.cat()耗时: {time_cat:.4f}秒\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 7.3 链式操作\n",
        "\n",
        "字符串拼接可以与其他字符串方法链式使用。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "0      python  \n",
            "1      PANDAS  \n",
            "2       numpy  \n",
            "dtype: object\n",
            "\n",
            "链式操作结果（去除空白 -> 转小写 -> 添加后缀）:\n",
            "0    python_library\n",
            "1    pandas_library\n",
            "2     numpy_library\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 链式操作示例\n",
        "data = pd.Series(['  python  ', '  PANDAS  ', '  numpy  '])\n",
        "\n",
        "print(\"原始数据:\")\n",
        "print(data)\n",
        "\n",
        "# 链式操作：去除空白 -> 转小写 -> 添加前缀\n",
        "result = data.str.strip().str.lower() + '_library'\n",
        "print(\"\\n链式操作结果（去除空白 -> 转小写 -> 添加后缀）:\")\n",
        "print(result)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 8. 总结\n",
        "\n",
        "### 8.1 关键要点\n",
        "\n",
        "1. **str.cat()**: 功能强大的字符串连接方法\n",
        "   - 可以连接同一Series的元素\n",
        "   - 可以连接多个Series（按行）\n",
        "   - 支持控制NaN处理和连接方式\n",
        "   - 适合复杂场景\n",
        "\n",
        "2. **str.join()**: 连接字符串的字符\n",
        "   - 在字符之间添加分隔符\n",
        "   - 适合格式化字符串\n",
        "   - 功能相对有限\n",
        "\n",
        "3. **+ 运算符**: 最直观的拼接方法\n",
        "   - 语法简单，易于理解\n",
        "   - 适合简单拼接场景\n",
        "   - 处理NaN需要额外操作\n",
        "\n",
        "### 8.2 应用场景\n",
        "\n",
        "- **数据组合**: 组合姓名、地址等复合字段\n",
        "- **格式化**: 格式化电话号码、日期等\n",
        "- **生成标识符**: 创建唯一ID、代码等\n",
        "- **文本生成**: 生成邮件、报告等文本内容\n",
        "- **数据展示**: 组合多个字段用于展示\n",
        "\n",
        "### 8.3 最佳实践\n",
        "\n",
        "- 简单拼接优先使用`+`运算符\n",
        "- 连接多个Series时使用`str.cat()`\n",
        "- 需要控制NaN时使用`str.cat()`的`na_rep`参数\n",
        "- 需要控制连接方式时使用`str.cat()`的`join`参数\n",
        "- 格式化字符串时可以使用`str.join()`或切片+拼接\n",
        "- 使用链式操作简化代码\n",
        "- 注意处理NaN值，避免意外结果\n",
        "\n",
        "### 8.4 下一步学习\n",
        "\n",
        "掌握了字符串拼接后，可以继续学习：\n",
        "- 字符串分割（str.split, str.partition）\n",
        "- 字符串替换（str.replace）\n",
        "- 字符串格式化（f-string, format）\n",
        "- 更多字符串操作方法\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "ml311",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.11.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
